<!-- 通过凭证和地址上传视频或图片 -->
<template>
  <!-- 视频展示区 -->
  <template v-for="(url,index) in video.videoViewUrl" :key="index">
    <div class="show-box" v-if="url">
      <div class="video-box">
        <videoPlayer :height="160" :width="160" :src="url" get-info get-cover @info="videoFn.infoBack($event,index)"  @cover="videoFn.coverBack($event,index)"></videoPlayer>
        <el-icon class="remove-icon" @click="videoFn.removeVideo(index)"><Delete /></el-icon>
      </div>

      <div class="cover-box">
        <div class="image-viewer" >
          <el-image class="image" :src="video.coverViewUrl[index]" fit="contain" @click="openImageViewer(video.coverViewUrl,index)"></el-image>
          <el-icon class="change-icon" @click="videoFn.reviseCoverFile(index)"><Refresh /></el-icon>
        </div>
      </div>

      <div class="videoinfo-box">
        {{videoStatic.videoInfos[index]?.title||"--"}}
      </div>

      <div class="upload-info" v-show="video.uploadStep!==0">
        <template v-if="video.uploadInfos[index]?.status===4">
          <!-- 上传完成 -->
          <div class="upload-result">
            <el-icon :size="16" color="#67C23A">
              <CircleCheckFilled />
            </el-icon>
            <span style="color:#67C23A;">视频上传完成</span>
          </div>
          <div class="upload-result" v-if="props.finishList[index]===1">
            <el-icon :size="16" color="#67C23A">
              <CircleCheckFilled />
            </el-icon>
            <span style="color:#67C23A;">作品上传完成</span>
          </div>
          <div class="upload-result" v-else-if="props.finishList[index]===2">
            <el-icon :size="16" color="#F56C6C">
              <CircleCloseFilled />
            </el-icon>
            <span style="color:#F56C6C;">作品上传失败</span>
          </div>
          <div class="upload-result" v-else>
            <el-icon :size="16" color="#409EFF">
              <Promotion />
            </el-icon>
            <span style="color:#409EFF;">等待作品上传</span>
          </div>
        </template>
        <template v-else-if="video.uploadInfos[index]?.status===5">
          <!-- 上传失败 -->
          <div class="upload-result">
            <el-icon :size="16" color="#F56C6C">
              <CircleCloseFilled />
            </el-icon>
            <span style="color:#F56C6C;">视频上传失败</span>
          </div>
        </template>
        <template v-else-if="video.uploadInfos[index]?.status===3">
          <!-- 获取凭证失败 -->
          <div class="upload-result">
            <el-icon :size="16" color="#E6A23C">
              <WarningFilled />
            </el-icon>
            <span style="color:#E6A23C;">获取凭证失败</span>
          </div>
        </template>
        <template v-else-if="video.uploadInfos[index]?.status===2">
          <!-- 上传进度显示 -->
          <p style="width:80%;color:#646464;line-height: 24px;">视频上传进度</p>
          <el-progress style="width:80%;" :text-inside="true" :stroke-width="16"  :percentage="video.uploadInfos[index]?.video||0" :color="[{ color: '#1989fa', percentage: 99 },{ color: '#67c23a', percentage: 100 }]" />
          <p style="width:80%;color:#646464;line-height: 24px;margin-top:16px;">封面上传进度</p>
          <el-progress style="width:80%;" :text-inside="true" :stroke-width="16"  :percentage="video.uploadInfos[index]?.cover||0" :color="[{ color: '#1989fa', percentage: 99 },{ color: '#67c23a', percentage: 100 }]" />
        </template>
        <template v-else-if="video.uploadInfos[index]?.status===1">
          <!-- 等待上传 -->
          <div class="upload-result">
            <el-icon :size="16" color="#409EFF">
              <Promotion />
            </el-icon>
            <span style="color:#409EFF;">等待视频上传</span>
          </div>
        </template>
      </div>
    </div>
  </template>

  <!-- 上传视频+视频上传input+图片上传input -->
  <div :class="['upload-box',props.disabled?'upload-disabled':'']" @click="videoFn.handleUpload">
    <el-icon class="upload-icon"><UploadFilled /></el-icon>
    <p class="upload-text">点击选择视频</p>
  </div>

  <input ref="uploadCoverInput" class="upload-input" type="file" accept="image/*" @change="videoFn.coverFileChange" />
  <input ref="uploadVideoInput" class="upload-input" type="file" accept="video/*" @change="videoFn.filesChange" multiple />
</template>

<script setup>
import { reactive, ref, watchEffect } from 'vue';
import { UploadFilled,Delete,Refresh,CircleCheckFilled,CircleCloseFilled,WarningFilled,Promotion } from '@element-plus/icons-vue'
import videoPlayer from "@/components/video/videoPlayer"
import { ElMessage } from 'element-plus';
import {getVodUploadAuth,refreshVodUploadAuth,getImageUploadAuth,getRegionAuth} from "@/api/operation";
import imageViewer from "@/components/imageViewer/index2";
const props = defineProps({
  uploadStatus: {
    default: "",//""||"uoload"||"clear",当状态变为upload时，触发上传;为clear时，清空数据
    type: String,
  },
  disabled:{
    default: false,
    type: Boolean,
  },
  finishList:{
    default: ()=>[],
    type: Array,
  }
})
const emit = defineEmits([
  "video:info","image:info",
  "video:ready","image:ready",
  "video:success","image:success",
  "video:fail","image:fail",
  "video:progress","image:progress",
  "video:number",
  "upload:finish",
  "update:uploadStatus",
  "clear",
])
const uploadVideoInput = ref(null);
const uploadCoverInput = ref(null);

const {$fn:aliyunFn} = (()=>{
  const $static={
    region: "cn-beijing",
    userId: "1183416238947919",//固定
  }
  const $fn={
    createUploader(type="video",index,info) {
      /**
       * type: "image"||"video"
       * index: number,数据序号，用于拼接数据
       * info: object,其他参数
       * */ 
      let {title,fileName} = info||{};
      const uploader = new AliyunUpload.Vod({
        timeout: 60000,
        partSize: 1048576,
        parallel: 5,
        retryCount: 3,
        retryDuration: 2,
        region: $static.region,
        userId: $static.userId,
        // 添加文件成功
        addFileSuccess(uploadInfo) {
          // console.log(index,"添加文件成功:",uploadInfo);
          emit(type+":ready",index);
        },
        // 开始上传
        onUploadstarted(uploadInfo) {
          // console.log("开始上传：",type,uploadInfo);
          if(type==="video"){
            if(!uploadInfo.videoId){
              getVodUploadAuth({title,fileName,index}).then(setVideoAuth).catch(dealError);
            }else{
              refreshVodUploadAuth({videoId:uploadInfo.videoId}).then(setVideoAuth).catch(dealError);
            }
          }else{
            getImageUploadAuth({imageType:"cover",index}).then(setImageAuth).catch(dealError);
          }

          function setVideoAuth(res){
            if(res.code===0){
              // 设置视频的凭证
              let {uploadAuth,uploadAddress,videoId} = res.data;
              uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress, videoId);
            }else{
              dealError();
            }
          }
          function setImageAuth(res){
            if(res.code===0){
              // 设置图片的凭证
              let {uploadAuth,uploadAddress,imageId} = res.data;
              uploader.setUploadAuthAndAddress(uploadInfo, uploadAuth, uploadAddress,imageId);
            }else{
              dealError();
            }
          }
          function dealError(err){
            uploader?.stopUpload();
            if(!video.uploadInfos[index].fail){
              video.finishedNum++;//失败时也将完成数+1
              video.uploadInfos[index].status = 3;
              video.uploadInfos[index].fail = true;
              emit(type+":fail",err,index);
              videoFn.runNextUploadContent();
            }
          }
        },
        // 文件上传成功
        onUploadSucceed (uploadInfo) {
          // console.log("**********上传成功：",uploadInfo);
          if(type==="video"){
            videoStatic.videoAuthInfos[index] = uploadInfo;
            if(videoStatic.coverAuthInfos[index]){
              // 此视频（包括封面）上传完成，进行下一个上传
              video.uploadInfos[index].status = 4;
              videoFn.runNextUploadContent();
              emit(type+":success",uploadInfo,index,true);
            }else{
              emit(type+":success",uploadInfo,index);
            }
            video.finishedNum++;
            if(video.finishedNum === video.licensedNum){
              emit("upload:finish");
            }
          }else{
            videoStatic.coverAuthInfos[index] = uploadInfo;
            if(videoStatic.videoAuthInfos[index]){
              // 此视频（包括封面）上传完成，进行下一个上传
              video.uploadInfos[index].status = 4;
              videoFn.runNextUploadContent();
              emit(type+":success",uploadInfo,index,true);
            }else{
              emit(type+":success",uploadInfo,index);
            }
          }
        },
        // 文件上传失败
        onUploadFailed (uploadInfo, code, message) {
          uploader?.stopUpload();
          if(!video.uploadInfos[index].fail){
            video.finishedNum++;//失败时也将完成数+1
            video.uploadInfos[index].fail = true;
            video.uploadInfos[index].status = 5;
            emit(type+":fail",message,index);
            videoFn.runNextUploadContent();
          }
        },
        // 取消文件上传
        // onUploadCanceled (uploadInfo, code, message) {
          
        // },
        // 文件上传进度，单位：字节, 可以在这个函数中拿到上传进度并显示在页面上
        onUploadProgress (uploadInfo, totalSize, progress) {
          // console.log("文件上传进度:",progress);
          let progressPercent = Math.ceil(progress * 100);
          video.uploadInfos[index][type==="video"?"video":"cover"] = progressPercent;
          emit(type+":progress",progress,index);
        },
        // 上传凭证超时
        onUploadTokenExpired (uploadInfo) {
          // console.log("上传凭证超时:",uploadInfo,index);
          if(type==="video"){
            // 根据videoid刷新凭证
            refreshVodUploadAuth({videoId:uploadInfo.videoId}).then(res=>{
              if(res.code!==0){return}
              let {uploadAuth} = res.data;
              uploader.resumeUploadWithAuth(uploadAuth);
            })
          }else{
            getImageUploadAuth({imageType:"cover"}).then(res=>{
              if(res.code!==0){return}
              let {uploadAuth} = res.data;
              uploader.resumeUploadWithAuth(uploadAuth);
            })
          }
        },
      });
      return uploader;
    },
  }

  getRegionAuth().then((res)=>{
    if(res.data?.regionId){
      $static.region = res.data.regionId;
    }
  });

  return {
    $fn,
  }
})();

const {$data: video,$fn:videoFn,$static:videoStatic} = (()=>{
  const $static = {
    videoFiles:[],//保存当前视频文件
    coverFiles:[],//保存当前封面文件【与videoFiles对应】

    videoInfos:[],//保存视频的信息（大小，尺寸，时长，标题等）【与videoFiles对应】
    coverInfos:[],//封面图的信息（尺寸）【与coverFiles对应】

    videoAuthInfos:[],//图片的上传信息（id，url）
    coverAuthInfos:[],//图片的上传信息（id，url）

    videoUploaders: [],//存放aliyun sdk loader对象
    coverUploaders: [],//存放aliyun sdk loader对象

    coverIndex: null,//修改封面时记录对应位置
    uploadIndex: 0,//正在上传的数据中index最大的值，如正在上传[2,5,9]，则index=9,表明(<=9)的数据已上传或正在上传，(>9)的数据未上传

    hintSize:false,//视频大小是否已经提示过？提示过则不再提示，除非新增视频和重置
    hintDuration:false,//视频长度是否已经提示过？提示过则不再提示，除非新增视频和重置
  }
  const $data = reactive({
    // isEligible: true,
    videoViewUrl:[],//视频的预览地址【与videoFiles对应】
    coverViewUrl:[],//封面图的预览地址【与coverFiles对应】

    uploadInfos: [],//上传进程信息{video:20,cover:99,stutus:0,fail:false},status=>1:等在上传,2:正在上传,3:获取凭证失败,4:上传成功,5:上传失败;fail:获取凭证是否失败

    uploadStep: 0,//0:未开始,1:进行中(有视频开始上传),2:进行中(全部视频都已开始或已完成上传),3:已完成
    licensedNum: 0,//上传的视频（有效的）数量
    finishedNum: 0,//已上传的视频数量

  })

  const $fn = {
    handleUpload() {
      if(props.disabled){return;}
      uploadVideoInput.value.click();
    },
    // 选择视频文件回调
    filesChange(e) {
      $static.hintSize = false;
      $static.hintDuration = false;
      const files = e.target.files||[];
      const GB10 = 10*1024*1024*1024;

      let count = 0, step = 0;
      const limit = 5;//每五个视频为一组，延迟加载
      for(let file of files){
        // 1.校验视频大小（小于10GB）
        if(file.size > GB10){
          if(!$static.hintSize){
            ElMessage.error("视频大小不能超过 10 Gb！已自动移除不符合的视频！");
            $static.hintSize = true;
          }
          continue;
        }
        $data.licensedNum++;
        // 2.添加到文件容器中
        $static.videoFiles.push(file);
        count++;
        step = ~~(count/limit);
        setTimeout(()=>{
          // 3.生成预览地址(异步延迟，每次处理5个)
          $data.videoViewUrl.push(getObjectURL(file));
        },300*step)
      }
      emit("video:number",$data.licensedNum);
      // 清空input的文件
      uploadVideoInput.value.value = "";
    },
    // 视频封面图回调
    coverBack(file,index){
      $static.coverFiles[index] = file;
      $data.coverViewUrl[index] = getObjectURL(file);
      // console.log("set url:",index,$data.coverViewUrl[index]);
      getImageInfo($data.coverViewUrl[index]).then(res=>{
        $static.coverInfos[index] = res;
        emit("image:info",res,index);
      })
    },
    // 视频信息回调
    infoBack(info,index){
      if(info.duration >= 10*60 || info.duration <= 5){
        if(!$static.duration){
          ElMessage.error("视频时长需大于5s，且小于10min！已自动移除不符合的视频！");
          $static.duration = true;
        }
        $static.videoFiles[index] = null;
        $data.videoViewUrl[index] = null;
        $data.coverViewUrl[index] = null;
        emit("video:number",--$data.licensedNum);
        return;
      }
      let title = filterSpecialCode($static.videoFiles[index].name.split(".")[0].slice(0,30));
      $static.videoInfos[index] = {...info,size: $static.videoFiles[index].size,title};
      emit("video:info",$static.videoInfos[index],index);
    },
    removeVideo(index){
      // 将files[]对应位置置空即可
      $static.videoFiles[index] = null;
      $data.videoViewUrl[index] = null;
      $data.coverViewUrl[index] = null;
      $data.licensedNum--;
      emit("video:number",$data.licensedNum);
      emit("video:info",null,index);
    },

    // 修改封面图
    reviseCoverFile(index){
      $static.coverIndex = index;
      uploadCoverInput.value.click();
    },
    coverFileChange(e) {
      let file = e.target.files[0];
      if(!file)return;
      let index = $static.coverIndex;
      $static.coverFiles[index] = file;
      // 1.生成预览的地址
      $data.coverViewUrl[index] = getObjectURL(file);
      // 2.获取封面图信息（尺寸）并保存
      getImageInfo($data.coverViewUrl[index]).then(res=>{
        $static.coverInfos[index] = res;
        emit("image:info",res,index);
      })
      $static.coverIndex = null;
    },

    // 准备上传，创建loader
    prepareUpload(){
      for(let i=0,len=$static.videoFiles.length;i<len;i++){
        let file = $static.videoFiles[i];
        let imgfile = $static.coverFiles[i];
        if(!file){continue;}
        let title = filterSpecialCode((file.name.split(".")[0]||"").slice(0,30));//限制标题长度为 30
        let fileName = title+"_"+Date.now()+".mp4";
        // 初始化上传进程
        $data.uploadInfos[i] = {video: 0,cover: 0,status: 1,fail:false,};
        
        // 创建video loader
        $static.videoUploaders[i] = aliyunFn.createUploader("video",i,{title,fileName});
        $static.videoUploaders[i].addFile(file, null, null, null, '{"Vod":{}}');
        // 创建image loader
        $static.coverUploaders[i] = aliyunFn.createUploader("image",i);
        $static.coverUploaders[i].addFile(imgfile, null, null, null, '{"Vod":{}}');
      }
      $data.uploadStep = 1;
      $fn.startUpload();
    },
    // 开始上传（触发skd上传）
    startUpload(){
      const Concurrency = 3;// 并发数
      let si=0;
      for(let i=0,len=$static.videoUploaders.length;i<len;i++){
        let loader = $static.videoUploaders[i];
        if(!loader){continue;}
        if(si>=Concurrency){break;}
        $fn.uploadContent(i);
        $static.uploadIndex = i;//更新末端index
        si++;
      }
    },
    uploadContent(index){
      console.log("step:","正在上传:index=",index);
      $data.uploadInfos[index].status = 2;
      $static.videoUploaders[index].startUpload();
      $static.coverUploaders[index].startUpload();
    },
    runNextUploadContent(){
      // [1,0,1,1,0] => 0 =>
      let index = ++$static.uploadIndex;
      if(index >= $static.videoUploaders.length){
        $data.uploadStep = 2;
      }else{
        if(!$static.videoUploaders[index]){
          $fn.runNextUploadContent();return;
        }
        $fn.uploadContent(index);
      }
    },

    clearUpload(){
      $static.videoFiles = [];
      $static.coverFiles = [];
      $static.videoInfos = [];
      $static.coverInfos = [];
      $static.videoAuthInfos = [];
      $static.coverAuthInfos = [];
      $static.videoUploaders = [];
      $static.coverUploaders = [];
      $static.coverIndex = null;
      $static.uploadIndex =  0;

      $data.videoViewUrl = [];
      $data.coverViewUrl = [];
      $data.uploadInfos = [];
      $data.uploadStep = 0;
      $data.licensedNum = 0;
      $data.finishedNum = 0;
    }
  }

  return {
    $static,
    $data,
    $fn,
  }
})();

watchEffect(()=>{
  if(props.uploadStatus === "upload"){
    console.log("step:","点击了上传");
    videoFn.prepareUpload();
  }else if(props.uploadStatus==="clear"){
    console.log("step:","清空了上传");
    videoFn.clearUpload();
  }
})

function getObjectURL(file) {
  let url = null;
  if (!!window.createObjectURL) { // basic
    url = window.createObjectURL(file);
  } else if (!!window.URL) { // mozilla(firefox)
    url = window.URL.createObjectURL(file);
  } else if (!!window.webkitURL) { // webkit or chrome
    url = window.webkitURL.createObjectURL(file);
  }
  return url;
}
function getImageInfo(url){
  return new Promise((resolve,reject)=>{
    const image = document.createElement("img");
    image.addEventListener("load",(e)=>{
      resolve({width:e.target.width,height:e.target.height});
    });
    image.src = url;
  })
}
function openImageViewer(list,index){
  if(!list?.[index]){return;}
  // 过滤空数据
  let inlist = [],inindex=index;
  for(let i=0,len = list.length;i<len;i++){
    let url = list[i];
    if(!url){
      index>i?inindex--:null;
      continue;
    }
    inlist.push(url);
  }
  imageViewer({initialIndex: inindex,urlList: inlist});
}

function filterSpecialCode(str){
  // let s = ['[',']'];
  return str.replaceAll(/\[|\]/g,"")
}

</script>

<style scoped lang="scss">
@mixin removeBtn(){
  position: absolute;
  right: 12px;
  top: 12px;
  width: 24px;
  height: 24px;
  color: #ffffff;
  border-radius: 50%;
  cursor: pointer;
  background-color: rgba(0, 0, 0, 0.3);
  &:hover{
    background-color: rgba(0, 0, 0, 0.5);
  }
}
// file input
.upload-input {
  display: none;
  z-index: -1;
}
.show-box{
  position: relative;
  display: inline-block;
  margin: 0 12px 12px 0;
  border-radius: 8px;
  overflow: hidden;
  border: 1px solid #299fff;
  line-height: 0;
  .video-box{
    position: relative;
    display: inline-block;
    margin-right: 8px;
    .remove-icon{
      @include removeBtn();
    }
  }
  .cover-box{
    width: 160px;
    height: 160px;
    display: inline-block;
    .upload-icon{
      margin-top: 40px;
    }
    .image-viewer{
      display: inline-block;
      position: relative;
      .change-icon{
        @include removeBtn();
      }
      .image{
        width:160px;
        height:160px;
        background-color:#f5f7fa;
      }
    }
  }
  &:hover{  
    .videoinfo-box{
      bottom: 0;
    }
  }
  .videoinfo-box{
    width: 100%;
    word-break: break-all;
    font-size: 14px;
    position: absolute;
    bottom: -24px;
    height: 24px;
    z-index: 999;
    line-height: 24px;
    color: #fff;
    background: #0606067a;
    padding: 0 4px;
    pointer-events: none;
    transition: 0.3s;
  }

  .upload-info{
    position: absolute;
    z-index: 99;
    width: 100%;
    height: 100%;
    left: 0;
    top: 0;
    background-color: rgba(255, 255, 255, 0.8);
    display: flex;
    backdrop-filter: blur(8px);
    flex-direction: column;
    justify-content: center;
    align-items: center;
    >.progress-box{
      width: 80%;
    }
    .upload-result{
      color: #fff;
      >i,>span{
        vertical-align: middle;
        margin: 0 4px;
      }
    }
  }
}

.upload-box{
  width: 160px;
  height: 160px;
  margin-right: 24px;
  vertical-align: top;
  position: relative;
  color: rgb(133, 133, 133);
  border: 1px dashed rgb(133, 133, 133);
  background-color: #ffffff;
  border-radius: 8px;
  display: inline-block;
  text-align: center;
  cursor: pointer;
  &:hover{
    color: #1890FF;
    border-color: #1890FF;
  }
  .upload-icon{
    display: block;
    width: 100%;
    font-size: 36px;
    margin-top: 40px;
  }
  .upload-text{
    margin: 8px 0;
  }

  &.upload-disabled{
    cursor: wait;
  }
}

</style>